﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using LitJson;
using System.Data;
using System.Threading;


namespace CoolChat
{
    public partial class MsgSrv : System.Web.UI.Page
    {
        
        static int mintick = 3000;//最少时间
        public static int timetick = mintick;
        static MsgSrv()
        {
            
        }

        public void OffLineTimer()
        {
            Thread offline = new Thread(new ThreadStart(OffLine));
            offline.Start();
            Application["OffLineTimer"] = offline;
        }
        void OffLine()
        {
            while (true)
            {
                List<ChatUser> li = Dbo.GetOffLineUser(timetick);
                if (null == Application["OffLineUser"])
                {
                    Application["OffLineUser"] = new List<ChatUser>();
                }
                ///
                ///保留1000个不在线用户的方法,暂时注释,测试时去掉注释
                ///
                List<ChatUser> offline = (List<ChatUser>)Application["OffLineUser"];
                if (offline.Count > 1000)
                {//保留1000个不在线用户
                    offline.RemoveRange(0, li.Count);
                }
                ///
                ///注意性能
                ///
                foreach (ChatUser u in li)
                {
                    u.LastLoginTime = DateTime.Now;
                    ChatUser isin = null;
                    foreach (ChatUser _u in offline)
                    {
                        if (_u.UserID == u.UserID)
                        {
                            isin = _u;
                        }
                    }
                    if (isin != null)
                    {
                        offline.Remove(isin);
                    }
                    offline.Add(u);
                    //////////////////////////
                    Dbo.AddOffLineMsg(u);
                }
                Thread.Sleep(timetick);
            }
        }
        
        protected void Page_Load(object sender, EventArgs e)
        {
            NewMethod();
        }

        private void NewMethod()
        {

            //注册DateTime类型如何转换为字符串
            JsonMapper.RegisterExporter(new ExporterFunc<DateTime>(Tools.DateTimeExForJson));
            Lang lang = new Lang();
            string act = Request["act"];//操作: send 发送一条消息, rev 接收消息, L 登陆
            string msg = Request["msg"];//发送消息时传入
            string ul = Request["ul"];//ul=1 返回在线用户列表, ul=0 返回不在线用户列表,ul=2 返回系统用户和用户列表(不管系统用户是否在线)
            string rjson = "";
            MsgReturn mreturn = new MsgReturn();
            if (string.IsNullOrEmpty(act) && string.IsNullOrEmpty(ul))
            {
                mreturn.Act = new KeyValuePair<ClientAct, string>[1] { new KeyValuePair<ClientAct, string>(ClientAct.ERROR, "we dont know what to do.there's no act") };
                rjson = JsonMapper.ToJson(mreturn);
                Response.Write(rjson);
                return;
            }
            ChatUser u = Tools.getCurUser();
            #region 处理用户操作请求
            USER_OPTION uop = USER_OPTION.NONE;
            try
            {
                uop = (USER_OPTION)int.Parse(ul);
            }
            catch
            {
                uop = USER_OPTION.NONE;
            }
            UserOption(mreturn, uop);
            #endregion
            switch (act)
            {//接收,发送或者登陆
                case "send":
                    ChatMessage cm = null;
                    try
                    {
                        msg.Replace("\"SilenceExpires\":null", "\"SilenceExpires\":\"2013-11-18 00:00:00\"");
                        cm = JsonMapper.ToObject<ChatMessage>(msg);
                    }
                    catch (JsonException jex)
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED, "object pass error" +"<br/>"+msg+"<br/>"+jex.ToString()) };
                        break;
                    }
                    try
                    {
                        if (u == null)
                        {
                            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED,lang["ZH_CN"]["RELOGIN"].ToString()) };
                            break;
                        }
                        if (Dbo.UpdateUserLoginState(u) == 0)
                        {//已经被追踪用户在线的线程标记为下线并且删除了...提示用户重新登陆吧..
                            Tools.setCurUser(null);
                            mreturn.Act = new KeyValuePair<ClientAct, string>[]{
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED, lang["ZH_CN"]["RELOGIN"].ToString())
                            };
                            break;
                        }
                        cm.From = u;
                        u.SilenceExpires = Dbo.GetUserSilence(u.UserID);
                        if (DateTime.Now < u.SilenceExpires)
                        {//用户被禁言
                            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED,"") };
                            mreturn.Silence = true;
                            break;
                        }
                        //mreturn.Silence = false;
                        if (string.IsNullOrEmpty(cm.MsgID))
                        {
                            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED,"msg id is null or empty") };
                            break;
                        }
                        if (cm.To == null)
                        {
                            cm.To = new ChatUser();
                            cm.To.UserID = Tools.md5(cm.MsgID + DateTime.Now, 16);
                            cm.To.UserName = "*";//* 代表所有人//lang["ZH_CN"]["ALL_BODY"].ToString();
                            cm.To.UserIP = "*";
                        }
                        cm.From.UserIP = Request.UserHostAddress;
                        int r = Dbo.AddMessage(cm);
                        if (r > 0)
                        {
                            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_SUCCESS,JsonMapper.ToJson(cm)) };
                        }
                        else
                        {
                            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED,JsonMapper.ToJson(cm)) };
                        }
                    }
                    catch (Exception ex)
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                            new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.MSG_FAILED,ex.ToString()) };
                    }
                    break;
                case "rev":
                    if (u == null)
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_ERROE,lang["ZH_CN"]["RELOGIN"].ToString()) };
                        break;
                    }
                    //在接收消息时,客户端发送一个参数指示是否更新数据库中的用户,通常,在第一次接收消息时,将用户更新进数据库
                    //以便在发送消息时调用
                    //mod: 0 返回一条消息,1返回条件之前的消息,2返回条件之后的消息.
                    //msgid 指定该参数将返回所有在这条消息之后的新消息.
                    //msgtime 指定该参数将返回所有在这个时间之后的消息. 日期类型
                    //msgaddtime 指定该参数将返回所有在这个时间之后添加的消息
                    //如果多个参数一起指定将会成 and 关系.
                    try
                    {
                        //mreturn.Silence = false;//设置为FALSE,否则一但设置为TRUE时就一直TRUE,还没找到原因
                        u.LastLoginTime = DateTime.Now;
                        if (Dbo.UpdateUserLoginState(u) == 0)
                        {//已经被追踪用户在线的线程标记为下线并且删除了...提示用户重新登陆吧..
                            Tools.setCurUser(null);
                            mreturn.Act = new KeyValuePair<ClientAct, string>[]{
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.LOGOFF, "")
                            };
                            break;
                        }
                        List<ChatMessage> msgs = new List<ChatMessage>();
                        double rtimeout = 5000;//单位毫秒
                        //int sleepTime = 500;//单位毫秒
                        double.TryParse(Request["timeout"], out rtimeout);//准备好长链接的超时时间
                        DateTime starttime = DateTime.Now;//记录当前时间
                        TimeSpan usetime = new TimeSpan(0);
                        if (Request["today"] == "true")
                        {
                            DateTime stoday = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
                            string type = null;
                            string endTime = null;
                            string keyWord = null;
                            string userName = null;
                            string order = "0";
                            string count = null;
                            msgs = Dbo.SelectMsgs(
                                string.IsNullOrEmpty(type) ? -1 : int.Parse(type),
                                stoday,
                                string.IsNullOrEmpty(endTime) ? DateTime.MinValue : DateTime.Parse(endTime),
                                keyWord, userName, string.IsNullOrEmpty(order) ? -1 : int.Parse(order),
                                string.IsNullOrEmpty(count) ? -1 : int.Parse(count));
                        }
                        else if (Request["last"] == "true")
                        {
                            msgs.Add(Dbo.GetLastMessage());
                        }
                        else
                        {
                            while (true)
                            {//长链接开始
                                u.LastLoginTime = DateTime.Now;
                                Dbo.UpdateUserLoginState(u);//更新用户的状态
                                string msgid = Request["msgid"];
                                string mod = Request["mod"];
                                string msgtime = Request["msgtime"];
                                string msgaddtime = Request["msgaddtime"];
                                msgs = Dbo.SelectMsgs(mod, msgid, msgtime, msgaddtime);
                                usetime = DateTime.Now - starttime;//计算出使用的时间
                                if (msgs.Count > 0 || usetime.TotalMilliseconds > rtimeout)
                                {
                                    break;
                                }
                            }
                        }
                        mreturn.Act = new KeyValuePair<ClientAct, string>[]{
                            new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_MSGS, JsonMapper.ToJson(msgs.ToArray()))
                        };
                    }
                    catch (Exception ex)
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                            new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_ERROE,ex.ToString()) };
                    }
                    break;
                case "L"://处理登陆
                    #region 处理登陆
                    Login(lang, mreturn);
                    #endregion
                    break;
                case "downmsgs":
                    if (u == null || u.UserRole != USER_ROLE.SA)
                    {
                        return;
                    }
                    DateTime s = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
                    DateTime et = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59);
                    DataSet ds = Dbo.SelectExportMsgs(s, et);
                    Tools.ExportMsgs(ds);
                    break;
                case "deletemsgs":
                    if (u == null || u.UserRole != USER_ROLE.SA)
                    {
                        Response.Write("{\"count\":-1}");
                        return;
                    }
                    DateTime debefore = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 0, 0, 0);
                    int deletecount = Dbo.DeleteMsgBeforeAddTime(debefore);
                    Response.Write("{\"count\":" + deletecount + "}");
                    return;
            }//接收,发送或者登陆的操作已经完成,需要输出处理结果或者处理其它操作,再输出处理结果
            
            //所有需要客户端响应的处理结果都应添加到 mreturn 中
            //现在输出 mreturn 
            rjson = JsonMapper.ToJson(mreturn);
            Response.Write(rjson);//输出处理结果
            //奇怪的问题,如果mreturn 没有被设置为 null,
            //那么下一次请求时,虽然实例化过 mreturn,但是mreturn.Silence还是为上次修改的值.
            //比如上次修改为TRUE,那么这次还将是TRUE,第三次时才会为初始值FALSE
            //所以加上这一句,保证下次实例出一个全新的对象
            mreturn = null;
        }

        /// <summary>
        /// 处理是否需要返回用户列表
        /// </summary>
        /// <param name="mreturn">指定返回时的对象</param>
        /// <param name="uop">操作用户的类型</param>
        private void UserOption(MsgReturn mreturn, USER_OPTION uop)
        {
            Lang lang = new Lang();
            switch (uop)//ul=0&tick=3000 这样就可以获取3000毫秒内活动过的在线用户列表
            {//在请求中加入参数 ul (USER_OPTION的值之一),加入或者不加入 参数 tick(数值,指定多少毫秒内算在线),来取得用户列表或其它用户操作.有些操作需要登陆
                case USER_OPTION.NONE:
                    break;
                case USER_OPTION.GET_ONLINE_USER:
                    if (Tools.getCurUser() == null)
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_ERROE,lang["ZH_CN"]["RELOGIN"].ToString()) };
                        break;
                    }
                    int itick = mintick;//默认3秒内活动的用户算在线
                    string tick = Request["tick"];
                    int.TryParse(tick, out itick);
                    itick = itick < mintick ? mintick : itick;
                    if (timetick != itick)
                        timetick = itick;
                    //启动用户下线的计时器,进行断定用户是否下线的操作
                    if (Application["OffLineTimer"] == null)
                    {
                        OffLineTimer();
                    }
                    ChatUser[] users = Dbo.GetOlineUser(itick).ToArray<ChatUser>();
                    UserListData udata = new UserListData();
                    udata.Act = UserListAct.FIXED;
                    udata.Data = users;
                    mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                            new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.USER_LIST,JsonMapper.ToJson(udata)) };
                    break;
                case USER_OPTION.SILENCE_USER:
                    ChatUser user = Tools.getCurUser();
                    if (user.UserRole != USER_ROLE.SA && (user.SysUserType != 1/*1: 为分析师,目前只对分析师开放权限*/ 
                        || user == null || user.UserRole != USER_ROLE.SYSUSER 
                        || user.UserRole != USER_ROLE.SA ))
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_ERROE,lang["ZH_CN"]["RELOGIN"].ToString()+" "+lang["ZH_CN"]["NO_RIGHT"].ToString()) };
                        break;
                    }
                    string uid = Request["uid"];
                    if (!string.IsNullOrEmpty(uid))
                    {
                        ChatUser usersilence = Dbo.getChatUser(uid);
                        //设置用户禁言时间
                        if (usersilence != null)
                        {
                            DateTime sl = DateTime.Now.AddDays(1);
                            Dbo.SetUserSilence(uid, sl);
                            mreturn.Silence = true;
                        }
                    }
                    break;
                case USER_OPTION.UNSILENCE_USER:                    
                    user = Tools.getCurUser();
                    if (user.UserRole != USER_ROLE.SA && (user.SysUserType != 1/*1: 为分析师,目前只对分析师开放权限*/ 
                        || user == null || user.UserRole != USER_ROLE.SYSUSER 
                        || user.UserRole != USER_ROLE.SA ))
                    {
                        mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.REVED_ERROE,lang["ZH_CN"]["RELOGIN"].ToString()+" "+lang["ZH_CN"]["NO_RIGHT"].ToString()) };
                        break;
                    }
                    uid = Request["uid"];
                    if (!string.IsNullOrEmpty(uid))
                    {
                        ChatUser usersilence = Dbo.getChatUser(uid);
                        //设置用户禁言时间
                        if (usersilence != null)
                        {
                            DateTime sl = DateTime.Now.AddDays(-1000);
                            Dbo.SetUserSilence(uid, sl);
                            mreturn.Silence = false;//成功返回
                        }
                    }
                    break;
                case USER_OPTION.GET_ONLINE_USER_AND_SYSUSER:
                    break;
                case USER_OPTION.GET_OFFLINE_USER:
                    UserListData uoff = new UserListData();
                    uoff.Act = UserListAct.OFFLINE;
                    List<ChatUser> offline = (List<ChatUser>)Application["OffLineUser"];
                    if (offline != null)
                        uoff.Data = offline.ToArray();
                    mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                            new KeyValuePair<ClientAct, string>(CoolChat.ClientAct.USER_LIST,JsonMapper.ToJson(uoff)) };
                    break;
                case USER_OPTION.GET_SYS_USER_TYPE:
                    string lan = Request["lan"];
                    if (string.IsNullOrEmpty(lan))
                    {
                        lan = "ZH_CN";
                    }
                    Response.Write(SysUserType.GetSysUserTypeNameJson(lan));
                    Response.End();
                    break;
            }
        }


        /// <summary>
        /// 处理登陆 如果用户没有登陆分配一个游客身份
        /// </summary>
        /// <param name="lang"></param>
        /// <param name="mreturn"></param>
        /// <returns></returns>
        private ChatUser Login(Lang lang, MsgReturn mreturn)
        {

            ChatUser u = null;// Tools.getCurUser();
            if (null == u)
            {//未Session的访客
                u = Tools.getCookieUser();
                if (null == u)
                {//未Cookie和Session的游客
                    ChatUser guser = new ChatUser();
                    guser.UserRole = USER_ROLE.SA;
                    guser.UserID = Tools.md5(Request.UserHostAddress + DateTime.Now.Ticks, 32);
                    guser.SessionId = Session.SessionID;
                    guser.UserIP = Request.UserHostAddress;
                    if (Request.UserHostAddress.IndexOf(':') > -1)
                    {//ipv6
                        guser.UserName = lang["ZH_CN"]["GUEST"] + Request.UserHostAddress
                            + " " + guser.UserID.Substring(guser.UserID.Length - 4);
                    }
                    else
                    {//ipv4
                        var f = guser.UserIP.Substring(0, guser.UserIP.IndexOf(".") + 1);
                        var b = guser.UserIP.Substring(guser.UserIP.IndexOf(".") + 1);
                        f += b.Substring(0, b.IndexOf("."));
                        guser.UserName = lang["ZH_CN"]["GUEST"] + " " + f + ".*.* " + guser.UserID.Substring(guser.UserID.Length - 4);
                    }
                    u = guser;
                    u.UserIP = Request.UserHostAddress;
                    u.Avatar = "";
                    Tools.setCurUser(u);
                    Tools.setCookieUser(u);
                }
                else
                {//Cookie的访客,更新Session
                    Tools.setCurUser(u);
                }
            }
            u.LastLoginTime = DateTime.Now;
            u.AddTime = DateTime.Now;
            if (Dbo.AddUserIfNotExists(u) > 0)
            {
                Session["chatuserlogin"] = null;
            }
            else if (Dbo.CheckUserOffLineByTime(timetick, u))
            {//用户已经不在线后再次连线
                Session["chatuserlogin"] = null;
            }
            object o = Session["chatuserlogin"];
            bool islogin = o == null ? false : (bool)o;
            if (!islogin)
            {//未登陆操作
                List<ChatUser> offline = (List<ChatUser>)Application["OffLineUser"];
                if (offline != null)
                {
                    offline.Remove(u);
                    u.UserIP = Request.UserHostAddress;
                    Dbo.AddOnLineMsg(u);
                    Session["chatuserlogin"] = true;
                }
            }
            Dbo.UpdateUserLoginState(u);
            //StopTackUserOffLine(u);
            //TackUserOffLine(u);
            mreturn.Act = new KeyValuePair<ClientAct, string>[1] { 
                                        new KeyValuePair<ClientAct,string>(ClientAct.RESET_USER,JsonMapper.ToJson(u)) };
            return u;
        }

        private void StopTackUserOffLine(ChatUser u)
        {
            Thread befth = (Thread)Session["threadoff"];
            if (befth != null)
            {
                befth.Abort();
                Session["threadoff"] = null;
            }
        }

        private void TackUserOffLine(ChatUser u)
        {
            Thread befth = (Thread)Session["threadoff"];
            if (befth == null || befth.ThreadState == ThreadState.Stopped || befth.ThreadState == ThreadState.Aborted)
            {
                Thread th = new Thread(new ParameterizedThreadStart(this.CheckOnline));
                th.Start(new KeyValuePair<ChatUser, System.Web.SessionState.HttpSessionState>(u, Session));
                Session["threadoff"] = th;
            }
        }

        public void CheckOnline(object session)
        {
            KeyValuePair<ChatUser, System.Web.SessionState.HttpSessionState> key = (KeyValuePair<ChatUser, System.Web.SessionState.HttpSessionState>)session;
            while (true)
            {
                Thread.Sleep(timetick);
                if (Dbo.GetOffLineUser(timetick, key.Key))
                {
                    key.Value["chatuserlogin"] = null;
                    Dbo.AddOffLineMsg(key.Key);
                    break;
                }
            }
        }

    }
}